<?php
/**
 * @author Matthijs van den Bos <matthijs@vandenbos.org>
 * @copyright 2013 Matthijs van den Bos
 */

namespace VDB\Spider\QueueManager;

use LDL\Twspider\C\ReentrantLock;
use VDB\Spider\Stackable;
use VDB\Spider\Uri\DiscoveredUri;
use VDB\Spider\Exception\QueueException;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\GenericEvent;
use VDB\Spider\Event\SpiderEvents;


use \Threaded as InData;

class InMemoryQueueManager extends \Volatile implements QueueManagerInterface, \Countable
{

    /** @var int The maximum size of the process queue for this spider. 0 means infinite */
    public $maxQueueSize = 0;

    /** @var int the amount of times a Resource was enqueued */
    private $currentQueueSize = 0;

    /** @var DiscoveredUri[] the list of URIs to process */
    private $traversalQueue;
    private $traversaledQueue;
    /** @var int The traversal algorithm to use. Choose from the class constants
     */
    private $traversalAlgorithm = self::ALGORITHM_DEPTH_FIRST;

    /** @var EventDispatcherInterface
     * where is use  ?
     **/
    private $dispatcher;
    private $lock;
    /**
     * @param int $traversalAlgorithm Choose from the class constants
     * TODO: This should be extracted to a Strategy pattern
     */
    public function setTraversalAlgorithm($traversalAlgorithm)
    {
        $this->traversalAlgorithm = $traversalAlgorithm;
    }

    public function __construct()
    {
        $this->traversalQueue = new InData();
      //  $this->traversaledQueue = new InData();
        $this->lock = new ReentrantLock();
    }

    /**
     * @return int
     */
    public function getTraversalAlgorithm()
    {
        return $this->traversalAlgorithm;
    }

    /**
     * @param EventDispatcherInterface $eventDispatcher
     * @return $this
     */
    public function setDispatcher(EventDispatcherInterface $eventDispatcher)
    {
        //die('whic used ????') ;
        $this->dispatcher = $eventDispatcher;

        return $this;
    }

    /**
     * @return EventDispatcherInterface
     */
    public function getDispatcher()
    {
        if (!$this->dispatcher) {
            $this->dispatcher = new EventDispatcher();
        }
        return $this->dispatcher;
    }

    /**
     * @param DiscoveredUri
     */
    public function addUri(DiscoveredUri $uri)
    {
        try{
            $this->lock->lock();
            if ($this->maxQueueSize != 0 && $this->currentQueueSize >= $this->maxQueueSize) {
                throw new QueueException('Maximum Queue Size of ' . $this->maxQueueSize . ' reached');
            }

            $this->currentQueueSize++;
            //$this[]=$uri;
            $this->traversalQueue[] = $uri;

            // var_dump($this->traversalQueue);
            $this->getDispatcher()->dispatch(
                SpiderEvents::SPIDER_CRAWL_POST_ENQUEUE,
                new GenericEvent($this, array('uri' => $uri))
            );

        }catch (QueueException $exception){
            throw $exception;
        }
        finally{

            $this->lock->unlock();

        }
    }

    public function next()
    {
        try{
            $this->lock->lock();
            //  return  $this->traversalQueue->synchronized(function(){
            $re = null;
            if($this->currentQueueSize <=0 ){
                return $re ;
            }
            $this->currentQueueSize--;
            if ($this->traversalAlgorithm === static::ALGORITHM_DEPTH_FIRST) {
                $re = $this->traversalQueue->pop();//array_pop($this->traversalQueue);
            } elseif ($this->traversalAlgorithm === static::ALGORITHM_BREADTH_FIRST) {
                $re = $this->traversalQueue->shift();
            } else {
                throw new \LogicException('No search algorithm set');
            }
            return $re;
        }catch (\LogicException $e){
            throw $e;
        }
        finally{

            $this->lock->unlock();

        }

    }


    /**
     * Count elements of an object
     * @link http://php.net/manual/en/countable.count.php
     * @return int The custom count as an integer.
     * </p>
     * <p>
     * The return value is cast to an integer.
     * @since 5.1.0
     */
    public function count()
    {
       // try{
            //$this->lock->lock();
            return $this->currentQueueSize;
           // return $this->traversalQueue->count();
      //  }finally{
          /*   var_dump($this->traversalQueue);
           var_dump($this->traversalQueue->count());
           var_dump($this->currentQueueSize);*/
           // $this->lock->unlock();
        //}
    }
}
